package us.versus.them.intersect;

/**
 * <p>
 * The functions intersect_triangle and intersect_triangle_non_culling
 * are based on "Fast, Minimum Storage Ray / Triangle Intersection" by
 * Tomas Möller and Ben Trumbore and ported directly from the copy
 * of the code on http://jgt.akpeters.com/papers/MollerTrumbore97/code.html
 * </p>
 *
 * @author Brian Hammond< brianin3d AT yahoo.com >
 *
 */
class Intersect {

	public static var EPSILON = 0.000001;

	public static inline function threePt() {
		return new Point( 0, 0, 0 );
	}

	public static inline function represent( x, y, z ) {
		return new Point( x, y, z );
	}

	// with culling
	// tuv:Point
	public static inline function intersect_triangle(
		orig:Point
		, dir:Point
		, vert0:Point
		, vert1:Point
		, vert2:Point
		, tuv:Point
	) {

		var edge1 = threePt();
		var edge2 = threePt();
		var tvec = threePt();
		var pvec = threePt();
		var qvec = threePt();
		var det : Float;
		var inv_det : Float;

		/* find vectors for two edges sharing vert0 */
		edge1.sub( vert1, vert0);
		edge2.sub( vert2, vert0);

		/* begin calculating determinant - also used to calculate U parameter */
		pvec.cross( dir, edge2 );

		/* if determinant is near zero, ray lies in plane of triangle */
		det = edge1.dot( pvec );

		/***********************/
		/* the culling branch */
		/*********************/

		var result = 0;

		if (det < EPSILON) {
			//return 0;
		} else {

			/* calculate distance from vert0 to ray origin */
			tvec.sub( orig, vert0 );

			/* calculate U parameter and test bounds */
			//*u = DOT(tvec, pvec);
			//if (*u < 0.0 || *u > det)
			tuv.y = tvec.dot( pvec );
			if (tuv.y < 0.0 || tuv.y > det) {
				//return 0;
			} else {

				/* prepare to test V parameter */
				qvec.cross( tvec, edge1);

				/* calculate V parameter and test bounds */
				//*v = DOT(dir, qvec);
				//if (*v < 0.0 || *u + *v > det)
				tuv.z = dir.dot( qvec );
				if ( tuv.z < 0.0 || tuv.y + tuv.z > det ) {
					//return 0;
				} else {
					/* calculate t, scale parameters, ray intersects triangle */
					//*t = DOT(edge2, qvec);
					tuv.x = edge2.dot( qvec );
					inv_det = 1.0 / det;
					// *t *= inv_det;
					// *u *= inv_det;
					// *v *= inv_det;
					tuv.scale( inv_det );
					result = 1;
					//return 1;
				}
			}
		}
		return result;
	}

	// tuv:Point
	public static inline function intersect_triangle_non_culling(
		orig:Point
		, dir:Point
		, vert0:Point
		, vert1:Point
		, vert2:Point
		, tuv:Point
	) {

		var edge1 = threePt();
	   	var edge2 = threePt();
		var pvec = threePt();
		var det : Float;

		/* find vectors for two edges sharing vert0 */
		edge1.sub( vert1, vert0 );
		edge2.sub( vert2, vert0 );

		/* begin calculating determinant - also used to calculate U parameter */
		pvec.cross( dir, edge2);

		/* if determinant is near zero, ray lies in plane of triangle */
		det = edge1.dot( pvec );

		/***************************/
		/* the non-culling branch */
		/*************************/

		var result = 0;

		if (det > -EPSILON && det < EPSILON) {
			//return 0;
		} else {
			var inv_det : Float;

			inv_det = 1.0 / det;

			var tvec = threePt();

			/* calculate distance from vert0 to ray origin */
			tvec.sub( orig, vert0);

			/* calculate U parameter and test bounds */
			//*u = DOT(tvec, pvec) * inv_det;
			//if (*u < 0.0 || *u > 1.0)
			tuv.y = ( tvec.dot( pvec) * inv_det );
			if ( tuv.y < 0 || tuv.y > 1.0 ) {
				//return 0;
			} else {
				
				var qvec = threePt();

				/* prepare to test V parameter */
				qvec.cross( tvec, edge1);

				/* calculate V parameter and test bounds */
				//*v = DOT(dir, qvec) * inv_det;
				//if (*v < 0.0 || *u + *v > 1.0)
				tuv.z = ( dir.dot( qvec ) * inv_det );
				if (tuv.z < 0.0 || tuv.y + tuv.z > 1.0) {
					//return 0;
				} else {
					/* calculate t, ray intersects triangle */
					//*t = DOT(edge2, qvec) * inv_det;
					tuv.x = ( edge2.dot( qvec ) * inv_det );
					result = 1;
				}
			}
		}
		return result;
	}

	public static inline function intersect_sphere(
		orig:Point
		, dir:Point
		, center:Point
		, radius:Float
	) {
		var source_to_sphere = threePt().sub( orig, center );
		var r2 = radius * radius;

		var dv = dir.dot( source_to_sphere );
		var d2 = dir.dot( dir );

		var determinant = (
			( dv * dv ) 
			- d2 * ( 
				source_to_sphere.dot( source_to_sphere ) - r2 
			)
		);

		var result = -1.0;

		if( determinant >= 0 ) {
			determinant = Math.sqrt( determinant );
			var t1 = ( -dv + determinant ) / d2;
			var t2 = ( -dv - determinant ) / d2;
			result = t1 < t2 ? t1 : t2;
			if ( result < 0 ) {
				result = t2;
			}
		}

		return result;
	}

}
